#include "frame.h"

/*
        MsgController
*/


MsgController* MsgController::mMsgController = NULL;

MsgController::~MsgController()
{
        mMsgController = NULL;
}
MsgController* MsgController::Instance()
{
        if ( !mMsgController ) {
                mMsgController = new MsgController;
        }
        return mMsgController;
}

LRESULT CALLBACK MsgController::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        //update user defined msgproc
        for ( int i = 0; i != MsgController::Instance()->mMsgs.size(); ++i ) {
                MsgController::Instance()->mMsgs[ i ]( message, wParam, lParam );
        }

        Mouse::Instance()->UpdateMouse( message, wParam, lParam );

        switch( message ) {
        case WM_DESTROY:
                {
                        PostQuitMessage(0);
                        return 0;
                }
        }

        return DefWindowProc (hWnd, message, wParam, lParam);
}

void MsgController::RegisterMsg( MsgFunc msgFunc )
{
        mMsgs.push_back( msgFunc );
}
/*
        Mouse
*/
Mouse* Mouse::mMouse = NULL;

Mouse::~Mouse()
{
        mMouse = NULL;
}

Mouse* Mouse::Instance()
{
        if ( !mMouse ) {
                mMouse = new Mouse;
        }
        return mMouse;
}

void Mouse::UpdateMouse( UINT message, WPARAM wParam, LPARAM lParam )
{
        switch ( message ) {
        case WM_MOUSEMOVE:
                mX = LOWORD( lParam );
                mY = HIWORD( lParam );
                break;
        case WM_LBUTTONDOWN:
                mLButtonDown = true;
                break;
        case WM_RBUTTONDOWN:
                mRButtonDown = true;
                break;
        default:
                mRButtonDown = false;
                mLButtonDown = false;
                return;
        }
        
}

UINT Mouse::X()
{
        return mX;
}

UINT Mouse::Y()
{
        return mY;
}


/*
        RenderFrame
*/

LPDIRECT3DDEVICE9 RenderFrame::mD3DDevice = NULL;
LPDIRECT3D9     RenderFrame::mD3DObject           = NULL;

LPDIRECT3DDEVICE9 RenderFrame::GetD3DDevice()
{
        return mD3DDevice;
}

LPDIRECT3D9 RenderFrame::GetD3DObject()
{
        return mD3DObject;
}

RenderFrame::RenderFrame():mInitFunc( NULL ),
                                                   mMainLoopFunc( NULL ),
                                                   mUpdateFunc( NULL ),
                                                   mReleaseFunc( NULL )
{
        
}

void RenderFrame::ReleaseD3D()
{
        SAFE_RELEASE( mD3DDevice );
        SAFE_RELEASE( mD3DObject );
}

void RenderFrame::Init( HINSTANCE hInstance, String appName )
{
        InitD3D( InitWnd( hInstance, appName ) );
        return OnInit();
}

void RenderFrame::Update( float dt )
{
        return OnUpdate( dt );
}

void RenderFrame::Render( )
{
	HR( mD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 37, 41, 40 ), 1.0f, 0 ) );

        OnMainLoop();

        HR( mD3DDevice->Present( NULL, NULL, NULL, NULL ) );
}

void RenderFrame::Run()
{
	MSG msg;

	__int64 cntsPerSec = 0;
	QueryPerformanceFrequency( (LARGE_INTEGER*)&cntsPerSec );
	float secsPerCnt = 1.0f / (float)cntsPerSec;

	__int64 prevTimeStamp = 0;
	QueryPerformanceCounter( (LARGE_INTEGER*)&prevTimeStamp );

	while( TRUE ) {
		while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) {
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}

                if( msg.message == WM_QUIT )
                        break;

		
		//TODO:count the seconds per frame
		__int64 currTimeStamp = 0;
		QueryPerformanceCounter( (LARGE_INTEGER*)&currTimeStamp );
		float dt = (currTimeStamp - prevTimeStamp)*secsPerCnt;

                Update( dt );
                Render();

		currTimeStamp = prevTimeStamp;
	}
	return Release();
}

void RenderFrame::Release()
{
        ReleaseD3D();
        return OnRelease();
}

void RenderFrame::RegisterInit( InitFunc initFunc ) 
{
        mInitFunc = initFunc;
}

void RenderFrame::RegisterMainLoop( MainLoopFunc mainLoopFunc )
{
        mMainLoopFunc = mainLoopFunc;
}

void RenderFrame::RegisterUpdate( UpdateFunc updateFunc )
{
        mUpdateFunc = updateFunc;
}

void RenderFrame::RegisterRelease( ReleaseFunc releaseFunc )
{
        mReleaseFunc = releaseFunc;
}

HWND RenderFrame::InitWnd( HINSTANCE hInstance, String appName )
{
        HWND hwnd;
        WNDCLASSEX wc;

        ZeroMemory( &wc, sizeof( WNDCLASSEX ) );

        wc.cbSize = sizeof( WNDCLASSEX );
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = MsgController::Instance()->WndProc;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor( NULL, IDC_ARROW );
        wc.lpszClassName = L"WindowClass";

		RECT in;
		in.left = 0; in.right = screen_width;
		in.top = 0; in.bottom = screen_height;
		AdjustWindowRect( &in, WS_OVERLAPPEDWINDOW, false );

        RegisterClassEx( &wc );
        hwnd = CreateWindowEx( NULL,
                        L"WindowClass",
                        appName,
                        WS_OVERLAPPEDWINDOW,
                        0, 0,
                        in.right - in.left, in.bottom - in.top,
                        NULL,
                        NULL,
                        hInstance,
                        NULL );

	

        ShowWindow( hwnd, SW_SHOW );
        return hwnd;
}

void RenderFrame::InitD3D( HWND hwnd )
{
        mD3DObject = Direct3DCreate9(D3D_SDK_VERSION);

        D3DPRESENT_PARAMETERS d3dpp;

        ZeroMemory( &d3dpp, sizeof( d3dpp ) );
        d3dpp.Windowed                                  = TRUE;
        d3dpp.SwapEffect                                = D3DSWAPEFFECT_DISCARD;
        d3dpp.hDeviceWindow                             = hwnd;
        d3dpp.BackBufferFormat							= D3DFMT_X8R8G8B8;
        d3dpp.AutoDepthStencilFormat					= D3DFMT_D24S8;
        d3dpp.BackBufferWidth							= screen_width;
        d3dpp.BackBufferHeight							= screen_height;
        d3dpp.EnableAutoDepthStencil					= TRUE;

	
	HR( mD3DObject->CreateDevice( D3DADAPTER_DEFAULT,
			D3DDEVTYPE_HAL,
			hwnd,
			D3DCREATE_HARDWARE_VERTEXPROCESSING,
			&d3dpp,
			&mD3DDevice ) );
}

void RenderFrame::OnInit( )
{
        if ( mInitFunc )
                mInitFunc();
}

void RenderFrame::OnMainLoop()
{
        if ( mMainLoopFunc )
                mMainLoopFunc();
}

void RenderFrame::OnRelease()
{
        if ( mReleaseFunc )
                mReleaseFunc();
}

void RenderFrame::OnUpdate( float dt )
{
        if ( mUpdateFunc )
                mUpdateFunc( dt );
}